/*******************************************************************************
* Copyright (c) 2015 Jeff Martin.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public
* License v3.0 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* Contributors:
* Jeff Martin - initial API and implementation
******************************************************************************/
package cuchaz.enigma.mapping;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Deque;
import com.google.common.collect.Queues;
public class MappingsReader
{
public Mappings read(Reader in) throws IOException, MappingParseException
{
return read(new BufferedReader(in));
}
public Mappings read(BufferedReader in) throws IOException,
MappingParseException
{
Mappings mappings = new Mappings();
Deque<Object> mappingStack = Queues.newArrayDeque();
int lineNumber = 0;
String line = null;
while((line = in.readLine()) != null)
{
lineNumber++;
// strip comments
int commentPos = line.indexOf('#');
if(commentPos >= 0)
line = line.substring(0, commentPos);
// skip blank lines
if(line.trim().length() <= 0)
continue;
// get the indent of this line
int indent = 0;
for(int i = 0; i < line.length(); i++)
{
if(line.charAt(i) != '\t')
break;
indent++;
}
// handle stack pops
while(indent < mappingStack.size())
mappingStack.pop();
String[] parts = line.trim().split("\\s");
try
{
// read the first token
String token = parts[0];
if(token.equalsIgnoreCase("CLASS"))
{
ClassMapping classMapping;
if(indent <= 0)
{
// outer class
classMapping = readClass(parts, false);
mappings.addClassMapping(classMapping);
}else
{
// inner class
if(!(mappingStack.peek() instanceof ClassMapping))
throw new MappingParseException(lineNumber,
"Unexpected CLASS entry here!");
classMapping = readClass(parts, true);
((ClassMapping)mappingStack.peek())
.addInnerClassMapping(classMapping);
}
mappingStack.push(classMapping);
}else if(token.equalsIgnoreCase("FIELD"))
{
if(mappingStack.isEmpty()
|| !(mappingStack.peek() instanceof ClassMapping))
throw new MappingParseException(lineNumber,
"Unexpected FIELD entry here!");
((ClassMapping)mappingStack.peek())
.addFieldMapping(readField(parts));
}else if(token.equalsIgnoreCase("METHOD"))
{
if(mappingStack.isEmpty()
|| !(mappingStack.peek() instanceof ClassMapping))
throw new MappingParseException(lineNumber,
"Unexpected METHOD entry here!");
MethodMapping methodMapping = readMethod(parts);
((ClassMapping)mappingStack.peek())
.addMethodMapping(methodMapping);
mappingStack.push(methodMapping);
}else if(token.equalsIgnoreCase("ARG"))
{
if(mappingStack.isEmpty()
|| !(mappingStack.peek() instanceof MethodMapping))
throw new MappingParseException(lineNumber,
"Unexpected ARG entry here!");
((MethodMapping)mappingStack.peek())
.addArgumentMapping(readArgument(parts));
}
}catch(ArrayIndexOutOfBoundsException | IllegalArgumentException ex)
{
throw new MappingParseException(lineNumber, "Malformed line:\n"
+ line);
}
}
return mappings;
}
private ArgumentMapping readArgument(String[] parts)
{
return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]);
}
private ClassMapping readClass(String[] parts, boolean makeSimple)
{
if(parts.length == 2)
return new ClassMapping(parts[1]);
else
return new ClassMapping(parts[1], parts[2]);
}
/* TEMP */
protected FieldMapping readField(String[] parts)
{
return new FieldMapping(parts[1], new Type(parts[3]), parts[2]);
}
private MethodMapping readMethod(String[] parts)
{
if(parts.length == 3)
return new MethodMapping(parts[1], new Signature(parts[2]));
else
return new MethodMapping(parts[1], new Signature(parts[3]),
parts[2]);
}
}